/*--------------------------------------------------------------------------
Modified from GNU Group's SDCC
Only for study purpose
---------------------------------------------------------------------------*/

#include "common.h"
#include "parser.h"

#include "SymbolTable.h"
#include "Support\NewAlloc.h"

/* yytext from yylex.c */
extern char *yytext;

symbol *currFunc = NULL;

bucket *SymbolTab[256];         /* the symbol    table  */
bucket *StructTab[256];         /* the structure table  */
bucket *TypedefTab[256];        /* the typedef   table  */
bucket *LabelTab[256];          /* the Label     table  */
bucket *enumTab[256];           /* enumerated    table  */

/*------------------------------------------------------------------*/
/* addDecl - adds a declarator @ the end of a chain                 */
/*------------------------------------------------------------------*/
void
addDecl (symbol * sym, int type, sym_link * p)
{
  static sym_link *empty = NULL;
  sym_link *head;
  sym_link *tail;
  sym_link *t;

  if (empty == NULL)
    empty = newLink(SPECIFIER);

  /* if we are passed a link then set head & tail */
  if (p)
    {
      tail = head = p;
      while (tail->next)
        tail = tail->next;
    }
  else
    {
      head = tail = newLink (DECLARATOR);
      DCL_TYPE (head) = type;
    }

  /* if this is the first entry   */
  if (!sym->type)
    {
      sym->type = head;
      sym->etype = tail;
    }
  else if (IS_SPEC (sym->etype) && IS_SPEC (head) && head == tail)
    {
      sym->etype = mergeSpec (sym->etype, head, sym->name);
    }
  else if (IS_SPEC (sym->etype) && !IS_SPEC (head) && head == tail)
    {
      t = sym->type;
      while (t->next != sym->etype)
        t = t->next;
      t->next = head;
      tail->next = sym->etype;
    }
  else if (IS_FUNC (sym->type) && IS_SPEC (sym->type->next) &&
           !memcmp(sym->type->next, empty, sizeof(sym_link)))
    {
      sym->type->next = head;
      sym->etype = tail;
    }
  else
    {
      sym->etype->next = head;
      sym->etype = tail;
    }

  /* if the type is an unknown pointer and has
     a tspec then take the storage class const & volatile
     attribute from the tspec & make it those of this
     symbol */
  if (p &&
      !IS_SPEC (p) &&
      //DCL_TYPE (p) == UPOINTER &&
      DCL_TSPEC (p))
    {
      if (!IS_SPEC (sym->etype))
        {
          sym->etype = sym->etype->next = newLink (SPECIFIER);
        }
      SPEC_SCLS (sym->etype) = SPEC_SCLS (DCL_TSPEC (p));
      DCL_TSPEC (p) = NULL;
    }

  // if there is a function in this type chain
  if (p && funcInChain(sym->type)) {
    processFuncArgs (sym);
  }

  return;
}
/*------------------------------------------------------------------*/
/* initSymt () - initialises symbol table related stuff             */
/*------------------------------------------------------------------*/
void
initSymt ()
{
	int i = 0;
	for (i = 0; i < 256; i++)
		SymbolTab[i] = StructTab[i] = (void *) NULL;
}
/*-----------------------------------------------------------------*/
/* newBucket - allocates & returns a new bucket                    */
/*-----------------------------------------------------------------*/
bucket *
newBucket ()
{
	bucket *bp;
	bp = Safe_alloc ( sizeof (bucket));
	return bp;
}

/*-----------------------------------------------------------------*/
/* hashKey - computes the hashkey given a symbol name              */
/*-----------------------------------------------------------------*/
int
hashKey (const char *s)
{
	unsigned long key = 0;

	while (*s)
		key += *s++;
	return key % 256;
}

/*-----------------------------------------------------------------*/
/* validateLink - validate link						               */
/*-----------------------------------------------------------------*/
sym_link *validateLink(sym_link         *l,
                        const char      *macro,
                        const char      *args,
                        const char      select,
                        const char      *file,
                        unsigned        line)
{
  if (l && l->_class==select)
    {
        return l;
    }
    fprintf(stderr,
            "Internal error: validateLink failed in %s(%s) @ %s:%u:"
            " expected %s, got %s\n",
            macro, args, file, line,
            DECLSPEC2TXT(select), l ? DECLSPEC2TXT(l->_class) : "null-link");
    exit(EXIT_FAILURE);
    return l; // never reached, makes compiler happy.
}

/*------------------------------------------------------------------*/
/* reverseLink - reverses the links for a type chain                */
/*------------------------------------------------------------------*/
sym_link *
reverseLink (sym_link * type)
{
  sym_link *prev, *curr, *next;

  if (!type)
    return NULL;

  prev = type;
  curr = type->next;

  while (curr)
    {
      next = curr->next;
      curr->next = prev;
      prev = curr;
      curr = next;
    }
  type->next = (void *) NULL;
  return prev;
}

/*------------------------------------------------------------------*/
/* copySymbolChain - copies a symbol chain                          */
/*------------------------------------------------------------------*/
symbol *
copySymbolChain (symbol * src)
{
  symbol *dest;

  if (!src)
    return NULL;

  dest = copySymbol (src);
  dest->next = copySymbolChain (src->next);
  return dest;
}

/*------------------------------------------------------------------*/
/* copySymbol - makes a copy of a symbol                            */
/*------------------------------------------------------------------*/
symbol *
copySymbol (symbol * src)
{
  symbol *dest;

  if (!src)
    return NULL;

  dest = newSymbol (src->name, src->level);
  memcpy (dest, src, sizeof (symbol));
  dest->level = src->level;
  dest->block = src->block;
  //dest->ival = copyIlist (src->ival);
  dest->type = copyLinkChain (src->type);
  dest->etype = getSpec (dest->type);
  dest->next = NULL;
  dest->key = src->key;
  dest->allocreq = src->allocreq;
  return dest;
}

/* noun strings */
char *nounName(sym_link *sl) {
  char *buff = (char *) calloc(256, sizeof(char));
	if (!IS_SPEC(sl))
	{
		free(buff);
		return "";
	}

  if (SPEC_USIGN(sl)) strcat(buff, "unsigned ");
  if (sl->select.s.b_signed) strcat(buff, "signed ");
  if (SPEC_VOLATILE(sl)) strcat(buff, "volatile ");
  if (SPEC_CONST(sl)) strcat(buff, "const ");
  if (SPEC_STAT(sl)) strcat(buff, "static ");

  switch (SPEC_NOUN(sl))
    {
    case V_INT: {
      if (SPEC_LONG(sl))
		  strcat(buff, "long");
	  else if (SPEC_SHORT(sl))
		  strcat(buff, "short");
	  else
		  strcat(buff, "int");
	  break;
    }
    case V_CHAR:
		strcat(buff, "char");
		break;
    case V_VOID:
		strcat(buff, "void");
		break;
    case V_STRUCT:
		strcat(buff, "struct");
		break;
    case V_LABEL:
		strcat(buff, "label");
		break;
    }

  return buff;
};

/* spec strings */
char *declName(sym_link *sl) {

  char *buff = (char *) calloc(128, sizeof(char));
	if (!IS_DECL(sl))
	{
		free(buff);
		return "";
	}

  switch (DCL_TYPE(sl))
    {
    case POINTER:
		free(buff);
		return "*";
    case ARRAY:
		sprintf(buff, "[%i]", DCL_ELEM(sl));
		return buff;
    case FUNCTION:
		free(buff);
		return "function";
    }

  return "";
};

char *printLinkChain(sym_link *sl)
{
	sym_link *type = reverseLink(copyLinkChain(sl));
	char *buff = (char *) calloc(256, sizeof(char));

	memset(buff, 0, 256);

	while (type)
	{
		if (IS_SPEC(type))
			strcat(buff, nounName(type));
		else if (IS_DECL(type))
			strcat(buff, declName(type));
		
		strcat(buff, " ");

		type = type->next;
	}

	return buff;
}

/*------------------------------------------------------------------
  checkTypeSanity: prevent the user from doing e.g.:
  unsigned float uf;
  ------------------------------------------------------------------*/
void checkTypeSanity(sym_link *etype, char *name) {
  char *noun;

  if (!etype) {
    if (getenv("DEBUG_SANITY")) {
      fprintf (stderr, "sanity check skipped for %s (etype==0)\n", name);
    }
    return;
  }

  if (!IS_SPEC(etype)) {
    if (getenv("DEBUG_SANITY")) {
      fprintf (stderr, "sanity check skipped for %s (!IS_SPEC)\n", name);
    }
    return;
  }

  noun=nounName(etype);

  if (getenv("DEBUG_SANITY")) {
    fprintf (stderr, "checking sanity for %s %p\n", name, etype);
  }

  if ((SPEC_NOUN(etype)==V_CHAR ||
       SPEC_NOUN(etype)==V_VOID) &&
      (SPEC_SHORT(etype) || SPEC_LONG(etype))) {
    // long or short for char float double or void
    yylexerror ("long or short type invalid, sym % has type %s", name, noun);
  }

  // special case for "short"
  if (SPEC_SHORT(etype)) {
    SPEC_NOUN(etype) = V_INT;
    SPEC_SHORT(etype) = 0;
  }

  /* if no noun e.g.
     "const a;" or "data b;" or "signed s" or "long l"
     assume an int */
  if (!SPEC_NOUN(etype)) {
    SPEC_NOUN(etype) = V_INT;
  }

  if (etype->select.s.b_signed && SPEC_USIGN(etype)) {
    // signed AND unsigned
    yylexerror ("signed AND unsigned type invalid, sym % has type %s", name, noun);
  }
  
  if (SPEC_SHORT(etype) && SPEC_LONG(etype)) {
    // short AND long
    yylexerror ("short AND long type invalid, sym % has type %s", name, noun);
  }
}

/*-----------------------------------------------------------------*/
/* addSym - adds a symbol to the hash Table                        */
/*-----------------------------------------------------------------*/
void
addSym (bucket ** stab,
		void *sym,
		char *sname,
		int level,
		int block,
		int checkType)
{
	int i;                        /* index into the hash Table */
	bucket *bp;                   /* temp bucket    *          */

	if (checkType) {
		symbol *csym = (symbol *)sym;
		
		if (getenv("DEBUG_SANITY")) {
			fprintf (stderr, "addSym: %s ", sname);
		}

		/* make sure the type is complete and sane */
		checkTypeSanity(csym->etype, csym->name);
	}

	/* the symbols are always added at the head of the list  */
	i = hashKey (sname);

	/* get a free entry */
	bp = Safe_alloc ( sizeof (bucket));

	bp->sym = sym;                /* update the symbol pointer */
	bp->level = level;            /* update the nest level     */
	bp->block = block;
	strncpy (bp->name, sname, sizeof(bp->name)); /* copy the name into place */

	/* if this is the first entry */
	if (stab[i] == NULL)
	{
		bp->prev = bp->next = (void *) NULL;      /* point to nothing */
		stab[i] = bp;
	}
	/* not first entry then add @ head of list */
	else
	{
		bp->prev = NULL;
		stab[i]->prev = bp;
		bp->next = stab[i];
		stab[i] = bp;
	}
}


/*-----------------------------------------------------------------*/
/* deleteSym - deletes a symbol from the hash Table entry          */
/*-----------------------------------------------------------------*/
void
deleteSym (bucket ** stab, void *sym, char *sname)
{
	int i = 0;
	bucket *bp;

	i = hashKey (sname);

	bp = stab[i];

	/* find the symbol */
	while (bp)
	{
		if (bp->sym == sym)       /* found it then break out */
			break;                /* of the loop       */
		bp = bp->next;
	}

	if (!bp)                      /* did not find it */
		return;

	/* if this is the first one in the chain */
	if (!bp->prev)
	{
		stab[i] = bp->next;
		if (stab[i])              /* if chain ! empty */
			stab[i]->prev = (void *) NULL;
	}
	/* middle || end of chain */
	else
	{
		if (bp->next)             /* if not end of chain */
			bp->next->prev = bp->prev;

		bp->prev->next = bp->next;
	}
}


/*--------------------------------------------------------------------*/
/* compareTypeExact - will do type check return 1 if match exactly    */
/*--------------------------------------------------------------------*/
int
compareTypeExact (sym_link * dest, sym_link * src, int level)
{
  STORAGE_CLASS srcScls, destScls;

  if (!dest && !src)
    return 1;

  if (dest && !src)
    return 0;

  if (src && !dest)
    return 0;

  /* if dest is a declarator then */
  if (IS_DECL (dest))
    {
      if (IS_DECL (src))
        {
          if (DCL_TYPE (src) == DCL_TYPE (dest)) {
            if ((DCL_TYPE (src) == ARRAY) && (DCL_ELEM (src) != DCL_ELEM (dest)))
              return 0;
            if (DCL_PTR_CONST (src) != DCL_PTR_CONST (dest))
              return 0;
            if (DCL_PTR_VOLATILE (src) != DCL_PTR_VOLATILE (dest))
              return 0;
            if (IS_FUNC(src))
              {
                value *exargs, *acargs, *checkValue;

                /* verify function return type */
                if (!compareTypeExact (dest->next, src->next, -1))
                  return 0;

                /* compare expected args with actual args */
                exargs = FUNC_ARGS(dest);
                acargs = FUNC_ARGS(src);

                /* for all the expected args do */
                for (; exargs && acargs; exargs = exargs->next, acargs = acargs->next);

				  /* if one them ended we have a problem */
				  if ((exargs && !acargs && !IS_VOID (exargs->type)) ||
					  (!exargs && acargs && !IS_VOID (acargs->type)))
					return 0;
				  return 1;
              }
            return compareTypeExact (dest->next, src->next, level);
          }
          return 0;
        }
      return 0;
    }

  /* if one is a specifier and the other is not */
  if ((IS_SPEC (src) && !IS_SPEC (dest)) ||
      (IS_SPEC (dest) && !IS_SPEC (src)))
    return 0;

  /* if one of them is a void then ok */
  if (SPEC_NOUN (dest) != SPEC_NOUN (src))
    return 0;

  if (IS_INTEGRAL (dest))
    {
      /* signedness must match */
      if (SPEC_USIGN (dest) != SPEC_USIGN (src))
        return 0;
      /* size must match */
      if (SPEC_LONG (dest) != SPEC_LONG (src))
        return 0;
      if (SPEC_SHORT (dest) != SPEC_SHORT (src))
        return 0;
    }

  if (IS_STRUCT (dest))
    {
      if (SPEC_STRUCT (dest) != SPEC_STRUCT (src))
        return 0;
    }

  if (SPEC_CONST (dest) != SPEC_CONST (src))
    return 0;
  if (SPEC_VOLATILE (dest) != SPEC_VOLATILE (src))
    return 0;
  if (SPEC_STAT (dest) != SPEC_STAT (src))
    return 0;

  destScls = SPEC_SCLS (dest);
  srcScls = SPEC_SCLS (src);

  if (srcScls != destScls)
    {
      return 0;
    }

  return 1;
}

/*------------------------------------------------------------------*/
/* addSymChain - adds a symbol chain to the symboltable             */
/*------------------------------------------------------------------*/
void
addSymChain (symbol ** symHead)
{
  symbol *sym = *symHead;
  symbol *csym = NULL;
  int error = 0;

  for (; sym != NULL; sym = sym->next)
    {
      checkTypeSanity(sym->etype, sym->name);

      /* if already exists in the symbol table on the same level */
      if ((csym = findSymWithLevel (SymbolTab, sym)) &&
          csym->level == sym->level)
        {
          /* if not formal parameter and not in file scope
             then show symbol redefined error
             else check if symbols have compatible types */
		  if (!sym->_isparm && sym->level == 0)
            error = 1;
		  else if (sym->_isparm)
			  continue;
          else
            {
              /* If the previous definition was for an array with incomplete
                 type, and the new definition has completed the type, update
                 the original type to match */
              if (IS_DECL(csym->type) && DCL_TYPE(csym->type)==ARRAY
                  && IS_DECL(sym->type) && DCL_TYPE(sym->type)==ARRAY)
                {
                  if (!DCL_ELEM(csym->type) && DCL_ELEM(sym->type))
                    DCL_ELEM(csym->type) = DCL_ELEM(sym->type);
                }

              error = 0;
            }

          if (error)
            {
              /* one definition extern ? */
              if (IS_EXTERN (csym->etype) || IS_EXTERN (sym->etype))
				  ccerror(sym->fileDef, sym->lineDef, "extern mismatch: %s", sym->name);
              else
				  ccerror(sym->fileDef, sym->lineDef, "duplicate idetifier: %s", sym->name);			
              continue;
            }

          if (!csym->cdef && !sym->cdef && IS_EXTERN (sym->etype))
            {
              /* if none of symbols is a compiler defined function
                 and at least one is not extern
                 then set the new symbol to non extern */
              SPEC_EXTR(sym->etype) = SPEC_EXTR(csym->etype);
            }
        }

      /* add new entry */
      addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 1);
    }
}

/*------------------------------------------------------------------*/
/* newLink - creates a new link (declarator, specifier)             */
/*------------------------------------------------------------------*/
sym_link *
newLink (SYM_LINK_CLASS select)
{
  sym_link *p;

  p = Safe_alloc ( sizeof (sym_link));
  p->_class = select;

  return p;
}

/*------------------------------------------------------------------*/
/* newStruct - creats a new structdef from the free list            */
/*------------------------------------------------------------------*/
structdef *
newStruct (char *tag)
{
  structdef *s;

  s = Safe_alloc ( sizeof (structdef));

  strncpyz (s->tag, tag, sizeof(s->tag));               /* copy the tag */
  return s;
}

/*------------------------------------------------------------------*/
/* newSymbol () - returns a new pointer to a symbol                 */
/*------------------------------------------------------------------*/
symbol *
newSymbol (char *name, int scope)
{
	symbol *sym;

	sym = Safe_alloc ( sizeof (symbol));

	strncpy (sym->name, name, sizeof(sym->name));	/* copy the name */
	sym->level = scope;								/* set the level */
	sym->block = currBlockno;
	sym->lineDef = lexLineno;						/* set the line number */
	sym->fileDef = lexFilename;
	return sym;
}



/*------------------------------------------------------------------*/
/* mergeSpec - merges two specifiers and returns the new one        */
/*------------------------------------------------------------------*/
sym_link *
mergeSpec (sym_link * dest, sym_link * src, char *name)
{
  if (!IS_SPEC(dest) || !IS_SPEC(src)) {
	yylexerror ("internal error: %s", yytext);
    // the show must go on
    return newIntLink();
  }

  if (SPEC_NOUN(src)) {
    if (!SPEC_NOUN(dest)) {
      SPEC_NOUN(dest) = SPEC_NOUN(src);
    } else {
      /* we shouldn't redeclare the type */
      if (getenv("DEBUG_SANITY")) {
        fprintf (stderr, "mergeSpec: ");
      }
	  printf("internal error: can't merge two or more data type, %s", name);
    }
  }

  if (SPEC_SCLS(src)) {
    /* if destination has no storage class */
    if (!SPEC_SCLS (dest) || SPEC_SCLS(dest)==S_REGISTER) {
      SPEC_SCLS (dest) = SPEC_SCLS (src);
    } else {
      if (getenv("DEBUG_SANITY")) {
        fprintf (stderr, "mergeSpec: ");
      }
      printf("internal error: can't merge two or more storage class, %s", name);
    }
  }

  /* copy all the specifications  */
  SPEC_LONG (dest) |= SPEC_LONG (src);
  SPEC_SHORT(dest) |= SPEC_SHORT(src);
  SPEC_USIGN (dest) |= SPEC_USIGN (src);
  dest->select.s.b_signed|=src->select.s.b_signed;
  SPEC_STAT (dest) |= SPEC_STAT (src);
  SPEC_EXTR (dest) |= SPEC_EXTR (src);
  SPEC_CONST(dest) |= SPEC_CONST (src);
  SPEC_VOLATILE (dest) |= SPEC_VOLATILE (src);
  SPEC_TYPEDEF (dest) |= SPEC_TYPEDEF (src);
  SPEC_ENUM (dest) |= SPEC_ENUM (src);

  if (IS_STRUCT (dest) && SPEC_STRUCT (dest) == NULL)
    SPEC_STRUCT (dest) = SPEC_STRUCT (src);

  return dest;
}

/*-----------------------------------------------------------------*/
/* processFuncArgs - does some processing with function args       */
/*-----------------------------------------------------------------*/
void
processFuncArgs (symbol * func)
{
  value *val;
  int pNum = 0;
  sym_link *funcType=func->type;

  /* find the function declaration within the type */
  while (funcType && !IS_FUNC(funcType))
    funcType=funcType->next;

  /* loop thru all the arguments   */
  val = FUNC_ARGS(funcType);

  /* if it is void then remove parameters */
  if (val && IS_VOID (val->type))
    {
      FUNC_ARGS(funcType) = NULL;
      return;
    }

  while (val)
    {
      /* if a symbolname is not given  */
      /* synthesize a variable name */
      if (!val->sym)
        {
          sprintf(val->name, "_%s_PARM_%d", func->name, pNum++);
          val->sym = newSymbol (val->name, 1);
          val->sym->type = copyLinkChain (val->type);
          val->sym->etype = getSpec (val->sym->type);
          val->sym->_isparm = 1;
          strncpyz (val->sym->rname, val->name, sizeof(val->sym->rname));
          //addSymChain (&val->sym);
        }
	  else
	  {
		  checkTypeSanity (val->etype, val->name);
		  sprintf(val->name, "_%s_PARM_%d", func->name, pNum++);
          strncpyz (val->sym->rname, val->name, sizeof(val->sym->rname));
          val->sym->_isparm = 1;
	  }

      val = val->next;
    }
}

/*------------------------------------------------------------------*/
/* funcInChain - DCL Type 'FUNCTION' found in type chain            */
/*------------------------------------------------------------------*/
int
funcInChain (sym_link * lnk)
{
  while (lnk)
    {
      if (IS_FUNC (lnk))
        return 1;
      lnk = lnk->next;
    }
  return 0;
}

/*------------------------------------------------------------------*/
/* reverseSyms - reverses the links for a symbol chain              */
/*------------------------------------------------------------------*/
symbol *
reverseSyms (symbol * sym)
{
	symbol *prev, *curr, *next;

	if (!sym)
		return NULL;

	prev = sym;
	curr = sym->next;

	while (curr)
	{
		next = curr->next;
		curr->next = prev;
		prev = curr;
		curr = next;
	}
	sym->next = (void *) NULL;
	return prev;
}

sym_link *
copyLinkChain (sym_link * p)
{
	sym_link *head, *curr, *loop;

	/* note: v_struct and v_struct->fields are not copied! */
	curr = p;
	head = loop = (curr ? newLink (p->_class) : (void *) NULL);
	while (curr)
	{
		memcpy (loop, curr, sizeof (sym_link));   /* copy it */
		loop->next = (curr->next ? newLink (curr->next->_class) : (void *) NULL);
		loop = loop->next;
		curr = curr->next;
	}

	return head;
}

/*------------------------------------------------------------------*/
/* pointerTypes - do the computation for the pointer types          */
/*------------------------------------------------------------------*/
void
pointerTypes (sym_link * ptr, sym_link * type)
{
}

/*------------------------------------------------------------------*/
/* getSize - returns size of a type chain in bytes                  */
/*------------------------------------------------------------------*/
unsigned int
getSize (sym_link * p)
{
	/* if nothing return 0 */
	if (!p)
		return 0;

	if (IS_SPEC (p))
	{                           /* if this is the specifier then */
		switch (SPEC_NOUN (p))
		{                       /* depending on the specifier type */
		case V_INT:
			return (IS_LONG (p) ? LONGSIZE : INTSIZE);
		case V_CHAR:
			return CHARSIZE;
		case V_VOID:
			return 0;
		case V_STRUCT:
			return SPEC_STRUCT (p)->size;
		case V_LABEL:
			return 0;
		default:
			return 0;
		}
	}

	/* this is a declarator */
	switch (DCL_TYPE (p))
	{
		case ARRAY:
			if (DCL_ELEM(p)) {
				return DCL_ELEM (p) * getSize (p->next);
			} else {
				//    werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
				//    "can not tell the size of an array[]");
				return 0;
			}
		case POINTER:
		case FUNCTION:
			return (PTRSIZE);

		default:
			return 0;
	}
	return 0;
}

/*-----------------------------------------------------------------*/
/* findSym - finds a symbol in a table                             */
/*-----------------------------------------------------------------*/
void *
findSym (bucket ** stab, void *sym, const char *sname)
{
  bucket *bp;

  bp = stab[hashKey (sname)];
  while (bp)
    {
      if (bp->sym == sym || strcmp (bp->name, sname) == 0)
        break;
      bp = bp->next;
    }

  return (bp ? bp->sym : (void *) NULL);
}

/*-----------------------------------------------------------------*/
/* findSymWithLevel - finds a symbol with a name & level           */
/*-----------------------------------------------------------------*/
void *
findSymWithLevel (bucket ** stab, symbol * sym)
{
	bucket *bp;

	bp = stab[hashKey (sym->name)];

	/**
	**  do the search from the head of the list since the
	**  elements are added at the head it is ensured that
	** we will find the deeper definitions before we find
	** the global ones. we need to check for symbols with
	** level <= to the level given, if levels match then block
	** numbers need to match as well
	**/
	while (bp)
	{
		if (strcmp (bp->name, sym->name) == 0 && bp->level <= sym->level)
		{
			/* if this is parameter then nothing else need to be checked */
			if (((symbol *) (bp->sym))->_isparm)
				return (bp->sym);
			/* if levels match then block numbers should also match */
			if (bp->level && bp->level == sym->level && bp->block == sym->block)
				return (bp->sym);
			/* if levels don't match then we are okay */
			if (bp->level && bp->level != sym->level && bp->block <= sym->block)
				return (bp->sym);
			/* if this is a global variable then we are ok too */
			if (bp->level == 0)
				return (bp->sym);
		}

		bp = bp->next;
	}

	return (void *) NULL;
}

/*-----------------------------------------------------------------*/
/* findSymWithBlock - finds a symbol with name in with a block     */
/*-----------------------------------------------------------------*/
void *
findSymWithBlock (bucket ** stab, symbol * sym, int block)
{
  bucket *bp;

  bp = stab[hashKey (sym->name)];
  while (bp)
    {
      if (strcmp (bp->name, sym->name) == 0 &&
          bp->block <= block)
        break;
      bp = bp->next;
    }

  return (bp ? bp->sym : (void *) NULL);
}

/*------------------------------------------------------------------*/
/* checkDecl - does semantic validation of a declaration            */
/*------------------------------------------------------------------*/
int
checkDecl (symbol * sym, int isProto)
{
/*
  checkSClass (sym, isProto);        // check the storage class 
  changePointer (sym->type);         // change pointers if required 

   //if this is an array without any dimension
   //then update the dimension from the initial value 
  if (IS_ARRAY (sym->type) && !DCL_ELEM (sym->type))
    DCL_ELEM (sym->type) = getNelements (sym->type, sym->ival);
*/
  return 1;	/* true for all */
}

/*------------------------------------------------------------------*/
/* getSpec - returns the specifier part from a declaration chain    */
/*------------------------------------------------------------------*/
sym_link *
getSpec (sym_link * p)
{
  sym_link *loop;

  loop = p;
  while (p && !(IS_SPEC (p)))
    p = p->next;

  return p;
}

/*------------------------------------------------------------------*/
/* bitsForType - returns # of bits required to store this type      */
/*------------------------------------------------------------------*/
unsigned int
bitsForType (sym_link * p)
{
  /* if nothing return 0 */
  if (!p)
    return 0;

  if (IS_SPEC (p))
    {                           /* if this is the specifier then */

      switch (SPEC_NOUN (p))
        {                       /* depending on the specifier type */
        case V_INT:
          return (IS_LONG (p) ? LONGSIZE * 8 : INTSIZE * 8);
        case V_CHAR:
          return CHARSIZE * 8;
        case V_VOID:
          return 0;
        case V_STRUCT:
          return SPEC_STRUCT (p)->size * 8;
        case V_LABEL:
          return 0;
        default:
          return 0;
        }
    }

  /* this is a specifier  */
  switch (DCL_TYPE (p))
    {
    case ARRAY:
      return DCL_ELEM (p) * getSize (p->next) * 8;
    case POINTER:
    case FUNCTION:
      return (PTRSIZE * 8);

    default:
      return 0;
    }
}


/*------------------------------------------------------------------*/
/* newLongLink() - new long type                                    */
/*------------------------------------------------------------------*/
sym_link *
newLongLink ()
{
  sym_link *p;

  p = newLink (SPECIFIER);
  SPEC_NOUN (p) = V_INT;
  SPEC_LONG (p) = 1;

  return p;
}

/*------------------------------------------------------------------*/
/* newIntLink() - creates an int type                               */
/*------------------------------------------------------------------*/
sym_link *
newIntLink ()
{
  sym_link *p;

  p = newLink (SPECIFIER);
  SPEC_NOUN (p) = V_INT;

  return p;
}

/*------------------------------------------------------------------*/
/* newCharLink() - creates an char type                             */
/*------------------------------------------------------------------*/
sym_link *
newCharLink ()
{
  sym_link *p;

  p = newLink (SPECIFIER);
  SPEC_NOUN (p) = V_CHAR;

  return p;
}

/*------------------------------------------------------------------*/
/* comparePtrType() - compare two pointer type                      */
/*------------------------------------------------------------------*/
int
comparePtrType (sym_link * dest, sym_link * src, bool bMustCast)
{
  int res;

  if (IS_VOID (src->next) && IS_VOID (dest->next))
    return bMustCast ? -1 : 1;
  if ((IS_VOID (src->next) && !IS_VOID (dest->next)) ||
      (!IS_VOID (src->next) && IS_VOID (dest->next)) )
    return -1;
  res = compareType (dest->next, src->next);
  if (res == 1)
    return bMustCast ? -1 : 1;
  else if (res == -2)
    return -2;
  else
    return 0;
}

/*--------------------------------------------------------------------*/
/* compareType - will do type check return 1 if match, 0 if no match, */
/*               -1 if castable, -2 if only signedness differs        */
/*--------------------------------------------------------------------*/
int
compareType (sym_link * dest, sym_link * src)
{
  if (!dest && !src)
    return 1;

  if (dest && !src)
    return 0;

  if (src && !dest)
    return 0;

  /* if dest is a declarator then */
  if (IS_DECL (dest))
    {
      if (IS_DECL (src))
        {
          /* function pointer */
          if (IS_PTR (dest) && IS_PTR (src))
            {
              if (IS_FUNC (src->next) && IS_VOID(dest->next))
                return -1;
              if (IS_FUNC (dest->next) && IS_VOID(src->next))
                return -1;
              return comparePtrType(dest, src, FALSE);
            }

          if (DCL_TYPE (src) == DCL_TYPE (dest))
            {
              return comparePtrType(dest, src, FALSE);
            }
          if (IS_PTR (dest) && IS_PTR (src) && IS_VOID(src->next))
            {
              return -1;
            }
          if (IS_PTR (dest) && IS_ARRAY (src))
            {
				return 0;
            }
          if (IS_PTR (dest) && IS_FUNC (dest->next) && IS_FUNC (src))
            return compareType (dest->next, src);
          return 0;
        }
      else if (IS_PTR (dest) && IS_INTEGRAL (src))
        return -1;
      else
        return 0;
    }

  if (IS_PTR (src) && IS_VOID (dest))
    return -1;

  /* if one is a specifier and the other is not */
  if ((IS_SPEC (src) && !IS_SPEC (dest)) ||
      (IS_SPEC (dest) && !IS_SPEC (src)))
    return 0;

  /* if one of them is a void then ok */
  if (SPEC_NOUN (dest) == V_VOID &&
      SPEC_NOUN (src) != V_VOID)
    return -1;

  if (SPEC_NOUN (dest) != V_VOID &&
      SPEC_NOUN (src) == V_VOID)
    return -1;

  /* it is a specifier */
  if (SPEC_NOUN (dest) != SPEC_NOUN (src))
    {
      if (SPEC_USIGN (dest) == SPEC_USIGN (src) &&
          IS_INTEGRAL (dest) && IS_INTEGRAL (src) &&
          getSize (dest) == getSize (src))
        return 1;
      else if (IS_ARITHMETIC (dest) && IS_ARITHMETIC (src))
        return -1;
      else
        return 0;
    }
  else if (IS_STRUCT (dest))
    {
      if (SPEC_STRUCT (dest) != SPEC_STRUCT (src))
        return 0;
      else
        return 1;
    }

  if (SPEC_LONG (dest) != SPEC_LONG (src))
    return -1;

  if (SPEC_USIGN (dest) != SPEC_USIGN (src))
    return -2;

  return 1;
}

/*------------------------------------------------------------------*/
/* cleanUpLevel - cleansup the symbol table specified for all the   */
/*                symbols in the given level                        */
/*------------------------------------------------------------------*/
void
cleanUpLevel (bucket ** table, int level)
{
  int i;
  bucket *chain;

  /* go thru the entire  table  */
  for (i = 0; i < 256; i++)
    {
      for (chain = table[i]; chain; chain = chain->next)
        {
          if (chain->level >= level)
            {
              deleteSym (table, chain->sym, chain->name);
            }
        }
    }
}


/*-------------------------------------------------------------------*/
/* promoteAnonStructs - promote anonymous struct/union's fields into */
/*                      an enclosing struct/union                    */
/*-------------------------------------------------------------------*/
void
promoteAnonStructs (int su, structdef * sdef)
{
  symbol *field;
  symbol *subfield;
  symbol **tofield;
  symbol *nextfield;
  symbol *dupfield;
  int base;

  tofield = &sdef->fields;
  field = sdef->fields;
  while (field)
    {
      nextfield = field->next;
      if (!*field->name && IS_STRUCT (field->type))
        {
          /* Found an anonymous struct/union. Replace it */
          /* with the fields it contains and adjust all  */
          /* the offsets */

          base = field->offset;
          subfield = copySymbolChain (SPEC_STRUCT (field->type)->fields);
          if (!subfield)
            continue;           /* just in case it's empty */

          *tofield = subfield;
          for (;;)
            {
              /* check for field name conflicts resulting from promotion */
              dupfield = sdef->fields;
              while (dupfield && dupfield != subfield)
                {
                  if (*subfield->name && !strcmp (dupfield->name, subfield->name))
                    {
						ccerror(subfield->fileDef, subfield->lineDef, "%s has duplicate member %s",
                                su==STRUCT ? "struct" : "union",
                                subfield->name);
                    }
                  dupfield = dupfield->next;
                }

              subfield->offset += base;
              if (subfield->next)
                subfield = subfield->next;
              else
                break;
            }
          subfield->next = nextfield;
          tofield = &subfield->next;
        }
      else
        tofield = &field->next;
      field = nextfield;
    }
}


/*------------------------------------------------------------------*/
/* compStructSize - computes the size of a structure                */
/*------------------------------------------------------------------*/
int
compStructSize (int su, structdef * sdef)
{
  int sum = 0, usum = 0;
  int bitOffset = 0;
  symbol *loop;

  /* for the identifiers  */
  loop = sdef->fields;
  while (loop) {

    /* create the internal name for this variable */
    sprintf (loop->rname, "_%s", loop->name);
    if (su == UNION) {
        sum = 0;
        bitOffset = 0;
    }
    SPEC_VOLATILE (loop->etype) |= (su == UNION ? 1 : 0);
 
	  /* This is a non-bit field. Make sure we are */
	  /* byte aligned first */
	  if (bitOffset) {
		sum++;
		loop->offset = (su == UNION ? sum = 0 : sum);
		bitOffset = 0;
	  }
	  loop->offset = sum;
	  checkDecl (loop, 1);
	  sum += getSize (loop->type);

    loop = loop->next;

    /* if union then size = sizeof largest field */
    if (su == UNION)
      {
        /* For UNION, round up after each field */
        sum += ((bitOffset+7)/8);
        usum = max (usum, sum);
      }
  }

  /* For STRUCT, round up after all fields processed */
  if (su != UNION)
    sum += ((bitOffset+7)/8);

  return (su == UNION ? usum : sum);
}


/*-------------------------------------------------------------------*/
/* genSymName - generates and returns a name used for anonymous vars */
/*-------------------------------------------------------------------*/
char *
genSymName (int level)
{
  static int gCount = 0;
  static char gname[MAX_NAME + 1];

  sprintf (gname, "__%04d%04d", level, gCount++);
  return gname;
}


/*------------------------------------------------------------------*/
/* cleanUpBlock - cleansup the symbol table specified for all the   */
/*                symbols in the given block                        */
/*------------------------------------------------------------------*/
void
cleanUpBlock (bucket ** table, int block)
{
  int i;
  bucket *chain;

  /* go thru the entire  table  */
  for (i = 0; i < 256; i++)
    {
      for (chain = table[i]; chain; chain = chain->next)
        {
          if (chain->block >= block)
            {
              deleteSym (table, chain->sym, chain->name);
            }
        }
    }
}

/*-----------------------------------------------------------------*/
/* createFunction - Add symbol to SymbolTab						   */
/*-----------------------------------------------------------------*/
symbol *createFunction(symbol *name)
{
	symbol *csym;
	
	/* check if the function name already in the symbol table */
	if ((csym = findSym (SymbolTab, NULL, name->name)))
	{
		name = csym;
		if (DCL_FUNC_DCLED(name->type))
			yylexerror("duplicate function: '%s'", name->name);
	}
	else
	{
		addSymChain (&name);
		allocVariables (name);
	}

	DCL_FUNC_DCLED(name->type) = 1;
	name->lastLine = lexLineno;
	currFunc = name;

	/* reset the stack pointer */
	stackPtr = -1;

	/* name needs to be mangled */
	sprintf(name->rname, "%s%s", "_", name->name);
	allocParms (FUNC_ARGS(name->type));   /* allocate the parameters */

	return name;
}

void viewTable(FILE *fp, bucket **tab, int startkey, int endkey)
{
	bucket *p;
	int i;

	for(i = startkey; i <= endkey; i++)
	{
		p = tab[i];
		if (p)
			fprintf(fp, "\nHash key %i", i);
		while (p)
		{
			if (!((symbol *)p->sym)->_isparm)
			{
				if (((symbol *)p->sym)->islocal && ((symbol *)p->sym)->localof != NULL)
					fprintf(fp, "\n - Sym %p, name '%s', level %i block %i, local of '%s'", p->sym, p->name, p->level, p->block, ((symbol *)p->sym)->localof->name);
				else
					fprintf(fp, "\n - Sym %p, name '%s', level %i block %i", p->sym, p->name, p->level, p->block);
			}
			else
				fprintf(fp, "\n - Sym %p, name '%s', level %i block %i, param of function '%s'", p->sym, p->name, p->level, p->block, ((symbol *)p->sym)->localof->name);
			p = p->next;
		}
	}
	fprintf(fp, "\n");
}

/*------------------------------------------------------------------*/
/* getStructElement - returns element of a tructure definition      */
/*------------------------------------------------------------------*/
symbol *getStructElement (structdef * sdef, symbol * sym)
{
  symbol *field;

  //if (!sdef)
	 //yylexerror("%s is not a struct.", sym->name, sdef->tag);
  assert(sdef);
  for (field = sdef->fields; field; field = field->next)
    if (strcmp (field->name, sym->name) == 0)
      return field;

  yylexerror("'%s' is not a member of '%s'.", sym->name, sdef->tag);

  return sdef->fields;
}


/*-----------------------------------------------------------------*/
/* resolveSymbols - resolve symbols from the table for functions   */
/*-----------------------------------------------------------------*/
symbol *resolveArgSymbols (symbol *sym, symbol *func)
{
	bucket *bp;
	symbol *fsym;
	bp = SymbolTab[hashKey (sym->name)];

	while (bp)
	{
		fsym = ((symbol *)bp->sym)->localof;
		if (strcmp (bp->name, sym->name) == 0 &&
			bp->level <= sym->level &&
			((symbol *)bp->sym)->_isparm && fsym &&
			(fsym == func || strcmp(fsym->name, func->name) == 0))
		{
			return (bp->sym);
		}

		bp = bp->next;
	}

	return (void *) NULL;	
}